package io.jsen.meta.diff.obj.complex;

import io.jsen.meta.diff.field.MetaField;
import io.jsen.meta.diff.obj.MetaObject;
import io.jsen.meta.diff.obj.abs.AbstractComplex;
import io.jsen.meta.diff.obj.special.MetaDelete;
import io.jsen.meta.diff.obj.special.MetaNull;
import io.jsen.meta.diff.support.MetaModifier;
import io.jsen.meta.diff.utils.Md5Util;
import lombok.EqualsAndHashCode;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeSet;

/**
 * @author jsen
 * @date 2021/4/8
 */
@EqualsAndHashCode(callSuper = true)
public class MetaMap<K> extends AbstractComplex<MetaMap<K>, Map<K, MetaObject<?, ?>>> {

    public MetaMap(MetaField field, Map<K, MetaObject<?, ?>> val) {
        super(field, val);
        this.modifier = MetaModifier.MAP;
        this.val.forEach((k, metaObject) -> metaObject.getMetaRelation().setParent(this));
    }

    @Override
    protected int calState(MetaMap<K> obj) {
        Map<K, MetaObject<?, ?>> self = isDelVal() ? Collections.emptyMap() : new HashMap<>(val);
        Map<K, MetaObject<?, ?>> other = new HashMap<>(obj.val);

        self.forEach((key, metaObject) -> {
            if (other.containsKey(key)) {
                metaObject.doDiff(other.get(key));
                other.remove(key);
            } else {
                metaObject.doDiff(new MetaNull(metaObject.getMetaRelation().getMetaField()));
            }
        });
        other.forEach((key, metaObject) -> {
            MetaDelete del = new MetaDelete(metaObject.getMetaRelation().getMetaField());
            del.getMetaRelation().setParent(this);
            del.doDiff(metaObject);
            val.put(key, del);
        });

        return calCollectionState(val.values());
    }

    @Override
    public String createIndex() {
        StringBuilder builder = new StringBuilder();
        TreeSet<String> sortIndex = new TreeSet<>();
        val.forEach((k, v) -> {
            sortIndex.add(k.toString());
            sortIndex.add(v.getIndex());
        });
        for (String index : sortIndex) {
            builder.append(index);
        }
        return Md5Util.md5(builder.toString());
    }

    @Override
    @SuppressWarnings("rawtypes")
    public MetaObject doDiff(MetaObject obj) {
        super.doDiff(obj);
        if (obj instanceof MetaNull) {
            val.forEach((key, metaObject) -> metaObject.doDiff(new MetaNull(
                    new MetaField(metaObject.getMetaRelation().getMetaField().getFieldDefine(),
                            metaObject.getMetaRelation().getMetaField().getKey())
            )));
        }
        return this;
    }
}
